Skip to content

Improve Batch Queries Scanner (#215)#221

Open
wjdwl002 wants to merge 1 commit into
doyensec:devfrom
wjdwl002:215-improve-batch-queries-scanner
Open

Improve Batch Queries Scanner (#215)#221
wjdwl002 wants to merge 1 commit into
doyensec:devfrom
wjdwl002:215-improve-batch-queries-scanner

Conversation

@wjdwl002

Copy link
Copy Markdown

Summary

Closes #215.

Adds an automated Batch Queries Scanner that probes whether a GraphQL
endpoint supports query batching, complementing the existing manual Batch
Queries tab. It detects two batching styles and surfaces results both as Burp
audit issues and in the InQL scan results tree.

Rationale

Previously InQL had no automated way to determine whether an endpoint supports
batching. Batching support is a meaningful finding because it enables
rate-limit bypass, brute-force amplification, and DoS via a single HTTP request.

What's included

  • BatchScanner.kt — sends two probes (__typename-based) and analyzes responses:
    • Alias batching: query { inql_batch_alias1: __typename inql_batch_alias2: __typename }
    • Array batching: a 2-element JSON array of query objects
  • BurpScannerCheck.kt — reports a LOW/CERTAIN AuditIssue per supported batch type during active audit.
  • ScanResultsTreeNode.kt — adds a "Batch Query Detection" node to the scan tree.
  • Config.kt — adds report.batch, report.batch.alias, report.batch.array toggles (default on).
  • build.gradle — adds JUnit 5 + Mockito test deps, useJUnitPlatform(), and a test source set.
  • BatchScannerTest.kt — 30 unit tests covering payload generation, response analysis, and edge cases.

Testing

./gradlew test --tests "inql.graphql.scanners.BatchScannerTest"BUILD SUCCESSFUL, 30 tests passing.

Notes

  • Unit tests cover the pure functions (payload generation + response analysis);
    the live scan() path depends on Burp.Montoya and is exercised at runtime.
  • montoya-api is declared compileOnly in the main module, so testCompileOnly/testRuntimeOnly
    entries were added to make it available on the test classpath.

@bartek-doyensec

Copy link
Copy Markdown
Contributor

Hello @wjdwl002!
Thank you for the PR!
A quick note on timing: we maintain this project in periodic sprints rather than continuously. We've got your PR in our backlog and will review it during our next scheduled maintenance window. We appreciate your help and your patience!

@bartek-doyensec

Copy link
Copy Markdown
Contributor

Hi @wjdwl002!
The build fails for me because of failing tests:

% task all                    
task: Task "Make sure resources/static/ exists" is up to date
task: Task "Build and copy webapp: voyager" is up to date
task: Task "Build and copy webapp: graphiql" is up to date
task: [Build kotlin app] ./gradlew build
[kotlin] Starting a Gradle Daemon (subsequent builds will be faster)
[kotlin] > Task :checkKotlinGradlePluginConfigurationErrors SKIPPED
[kotlin] > Task :processResources UP-TO-DATE
[kotlin] > Task :processTestResources NO-SOURCE
[kotlin] > Task :compileKotlin
[kotlin] > Task :compileJava NO-SOURCE
[kotlin] > Task :classes UP-TO-DATE
[kotlin] > Task :jar
[kotlin] > Task :assemble
[kotlin] > Task :compileTestKotlin
[kotlin] > Task :compileTestJava NO-SOURCE
[kotlin] > Task :testClasses UP-TO-DATE
[kotlin] 
[kotlin] > Task :test FAILED
[kotlin] 
[kotlin] BatchScannerTest > Alias batch response analysis > detects not supported when response is not JSON() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:150
[kotlin]         Caused by: java.lang.IllegalStateException at BatchScannerTest.kt:150
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at BatchScannerTest.kt:150
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at BatchScannerTest.kt:150
[kotlin]                     Caused by: java.lang.IllegalStateException at BatchScannerTest.kt:150
[kotlin] 
[kotlin] BatchScannerTest > Alias batch response analysis > detects not supported when data field is missing() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:159
[kotlin]         Caused by: java.lang.IllegalStateException at BatchScannerTest.kt:150
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at BatchScannerTest.kt:150
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at BatchScannerTest.kt:150
[kotlin]                     Caused by: java.lang.IllegalStateException at BatchScannerTest.kt:150
[kotlin] 
[kotlin] BatchScannerTest > Alias batch response analysis > succeeds when errors array is empty() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:193
[kotlin]         Caused by: java.lang.IllegalStateException at BatchScannerTest.kt:150
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at BatchScannerTest.kt:150
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at BatchScannerTest.kt:150
[kotlin]                     Caused by: java.lang.IllegalStateException at BatchScannerTest.kt:150
[kotlin] 
[kotlin] BatchScannerTest > Alias batch response analysis > reports warning when aliases resolve but errors are present() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:176
[kotlin]         Caused by: java.lang.IllegalStateException at BatchScannerTest.kt:150
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at BatchScannerTest.kt:150
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at BatchScannerTest.kt:150
[kotlin]                     Caused by: java.lang.IllegalStateException at BatchScannerTest.kt:150
[kotlin] 
[kotlin] BatchScannerTest > Alias batch response analysis > detects not supported when only one alias present() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:127
[kotlin]         Caused by: java.lang.IllegalStateException at BatchScannerTest.kt:150
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at BatchScannerTest.kt:150
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at BatchScannerTest.kt:150
[kotlin]                     Caused by: java.lang.IllegalStateException at BatchScannerTest.kt:150
[kotlin] 
[kotlin] BatchScannerTest > Alias batch response analysis > detects supported when both aliases present in data() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:113
[kotlin]         Caused by: java.lang.IllegalStateException at BatchScannerTest.kt:150
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at BatchScannerTest.kt:150
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at BatchScannerTest.kt:150
[kotlin]                     Caused by: java.lang.IllegalStateException at BatchScannerTest.kt:150
[kotlin] 
[kotlin] BatchScannerTest > Alias batch response analysis > detects not supported on HTTP 400() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:135
[kotlin]         Caused by: java.lang.IllegalStateException at BatchScannerTest.kt:150
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at BatchScannerTest.kt:150
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at BatchScannerTest.kt:150
[kotlin]                     Caused by: java.lang.IllegalStateException at BatchScannerTest.kt:150
[kotlin] 
[kotlin] BatchScannerTest > Alias batch response analysis > detects not supported on HTTP 500() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:143
[kotlin]         Caused by: java.lang.IllegalStateException at BatchScannerTest.kt:150
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at BatchScannerTest.kt:150
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at BatchScannerTest.kt:150
[kotlin]                     Caused by: java.lang.IllegalStateException at BatchScannerTest.kt:150
[kotlin] 
[kotlin] BatchScannerTest > Alias batch response analysis > handles null data values gracefully() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:208
[kotlin]         Caused by: java.lang.IllegalStateException at BatchScannerTest.kt:150
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at BatchScannerTest.kt:150
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at BatchScannerTest.kt:150
[kotlin]                     Caused by: java.lang.IllegalStateException at BatchScannerTest.kt:150
[kotlin] 
[kotlin] BatchScannerTest > Array batch response analysis > handles empty response body() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:294
[kotlin]         Caused by: java.lang.IllegalStateException at DefaultMockitoPlugins.java:105
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at null:-1
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at InlineDelegateByteBuddyMockMaker.java:260
[kotlin]                     Caused by: java.lang.IllegalStateException at ByteBuddyAgent.java:632
[kotlin] 
[kotlin] BatchScannerTest > Array batch response analysis > detects supported when response is array of 2 results() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:230
[kotlin]         Caused by: java.lang.IllegalStateException at DefaultMockitoPlugins.java:105
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at null:-1
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at InlineDelegateByteBuddyMockMaker.java:260
[kotlin]                     Caused by: java.lang.IllegalStateException at ByteBuddyAgent.java:632
[kotlin] 
[kotlin] BatchScannerTest > Array batch response analysis > handles HTTP 405 Method Not Allowed() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:302
[kotlin]         Caused by: java.lang.IllegalStateException at DefaultMockitoPlugins.java:105
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at null:-1
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at InlineDelegateByteBuddyMockMaker.java:260
[kotlin]                     Caused by: java.lang.IllegalStateException at ByteBuddyAgent.java:632
[kotlin] 
[kotlin] BatchScannerTest > Array batch response analysis > detects not supported when server returns single object() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:238
[kotlin]         Caused by: java.lang.IllegalStateException at DefaultMockitoPlugins.java:105
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at null:-1
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at InlineDelegateByteBuddyMockMaker.java:260
[kotlin]                     Caused by: java.lang.IllegalStateException at ByteBuddyAgent.java:632
[kotlin] 
[kotlin] BatchScannerTest > Array batch response analysis > accepts response with extra fields in array elements() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:315
[kotlin]         Caused by: java.lang.IllegalStateException at DefaultMockitoPlugins.java:105
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at null:-1
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at InlineDelegateByteBuddyMockMaker.java:260
[kotlin]                     Caused by: java.lang.IllegalStateException at ByteBuddyAgent.java:632
[kotlin] 
[kotlin] BatchScannerTest > Array batch response analysis > detects not supported when array has wrong count() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:272
[kotlin]         Caused by: java.lang.IllegalStateException at DefaultMockitoPlugins.java:105
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at null:-1
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at InlineDelegateByteBuddyMockMaker.java:260
[kotlin]                     Caused by: java.lang.IllegalStateException at ByteBuddyAgent.java:632
[kotlin] 
[kotlin] BatchScannerTest > Array batch response analysis > detects not supported when server returns error object() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:251
[kotlin]         Caused by: java.lang.IllegalStateException at DefaultMockitoPlugins.java:105
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at null:-1
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at InlineDelegateByteBuddyMockMaker.java:260
[kotlin]                     Caused by: java.lang.IllegalStateException at ByteBuddyAgent.java:632
[kotlin] 
[kotlin] BatchScannerTest > Array batch response analysis > detects not supported when array elements lack data field() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:286
[kotlin]         Caused by: java.lang.IllegalStateException at DefaultMockitoPlugins.java:105
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at null:-1
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at InlineDelegateByteBuddyMockMaker.java:260
[kotlin]                     Caused by: java.lang.IllegalStateException at ByteBuddyAgent.java:632
[kotlin] 
[kotlin] BatchScannerTest > Array batch response analysis > detects not supported on HTTP 400() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:259
[kotlin]         Caused by: java.lang.IllegalStateException at DefaultMockitoPlugins.java:105
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at null:-1
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at InlineDelegateByteBuddyMockMaker.java:260
[kotlin]                     Caused by: java.lang.IllegalStateException at ByteBuddyAgent.java:632
[kotlin] 
[kotlin] BatchScannerTest > Edge cases > alias analysis handles deeply nested data() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:339
[kotlin]         Caused by: java.lang.IllegalStateException at DefaultMockitoPlugins.java:105
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at null:-1
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at InlineDelegateByteBuddyMockMaker.java:260
[kotlin]                     Caused by: java.lang.IllegalStateException at ByteBuddyAgent.java:632
[kotlin] 
[kotlin] BatchScannerTest > Edge cases > array analysis handles mixed success and error elements() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:368
[kotlin]         Caused by: java.lang.IllegalStateException at DefaultMockitoPlugins.java:105
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at null:-1
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at InlineDelegateByteBuddyMockMaker.java:260
[kotlin]                     Caused by: java.lang.IllegalStateException at ByteBuddyAgent.java:632
[kotlin] 
[kotlin] BatchScannerTest > Edge cases > alias analysis handles numeric values() FAILED
[kotlin]     java.lang.IllegalStateException at BatchScannerTest.kt:354
[kotlin]         Caused by: java.lang.IllegalStateException at DefaultMockitoPlugins.java:105
[kotlin]             Caused by: java.lang.reflect.InvocationTargetException at null:-1
[kotlin]                 Caused by: org.mockito.exceptions.base.MockitoInitializationException at InlineDelegateByteBuddyMockMaker.java:260
[kotlin]                     Caused by: java.lang.IllegalStateException at ByteBuddyAgent.java:632
[kotlin] 
[kotlin] 30 tests completed, 21 failed
[kotlin] 
[kotlin] FAILURE: Build failed with an exception.
[kotlin] 
[kotlin] * What went wrong:
[kotlin] Execution failed for task ':test'.
[kotlin] > There were failing tests. See the report at: 
[kotlin] 
[kotlin] * Try:
[kotlin] > Run with --scan to get full insights.
[kotlin] 
[kotlin] BUILD FAILED in 12s
[kotlin] 5 actionable tasks: 4 executed, 1 up-to-date
task: Failed to run task "all": exit status 1

Please ensure everything works as expected and let me know if any special setup is needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants